
/**
 * AppleIIGo
 * Floppy Disk Drive class
 * (C) 2008 by Marc S. Ressl (mressl@umich.edu)
 * Released under the GPL
 */

#include <stdlib.h>
#include "FloppyDiskDrive.h"

/**
 * Floppy Disk Drive Notes
 *
 * A floppy disk drive has a head stepper motor with 4 discretely controlled
 * magnetic phases. Of the 16 possible head phase states, 12 map to 8 net
 * phase vectors, 3 to undefined behaviour, and one to the off state.
 * The stepper motor has an inertial time constant of approx. 2 ms.
 */

FloppyDiskDrive::FloppyDiskDrive(UINT32 diskDriveType) :
DiskPort() {
	this->diskDriveType = diskDriveType;
	
	diskImage = 0;
	
	phaseRegister = 0;
	
	isDriveEnabled = false;
	isWriteEnabled = false;
	isDiskModified = false;
	
	mount(0);
}

FloppyDiskDrive::~FloppyDiskDrive() {
	delete diskImage;
}

bool FloppyDiskDrive::mount(char * diskImagePath) {
	FloppyDiskImage * diskImage;
	UINT32 diskType;
	if (diskDriveType == DISKDRIVETYPE_3_5)
		diskType = FLOPPYDISKTYPE_3_5;
	else
		diskType = FLOPPYDISKTYPE_5_25;
	diskImage = FloppyDiskImage::getInstance(diskType, diskImagePath);
	
	if (!diskImage)
		return false;
	
	setDriveEnabled(false);
	
	if (this->diskImage) {
		diskImage->setTrackIndex(this->diskImage->getTrackIndex());
		diskImage->setHeadIndex(this->diskImage->getHeadIndex());
		delete this->diskImage;
	} else {
		diskImage->setTrackIndex(0);
		diskImage->setHeadIndex(0);
	}
	
	this->diskImage = diskImage;
	
	setDriveEnabled(isDriveEnabled);
	
	updateDiskTrack();
	
	return diskImage;
}

void FloppyDiskDrive::unmount() {
	mount(0);
}

char * FloppyDiskDrive::getDiskImagePath() {
	return diskImage->getDiskImagePath();
}

void FloppyDiskDrive::setPhaseRegister(UINT32 value) {
	phaseRegister = value;
}

void FloppyDiskDrive::setDriveEnabled(bool value) {
	isDriveEnabled = value;
	
	if (!value) {
		if (isDiskModified) {
			diskImage->setModified();
			isDiskModified = false;
		}
	}
}

void FloppyDiskDrive::updateSendBitNum(UINT32 * value) {
	if (*value >= trackBitNum)
		*value = trackBitNum + (*value % trackBitNum);
}

void FloppyDiskDrive::sendBit(bool value) {
	if (isWriteEnabled) {
		UINT32 byteIndex = (trackBitIndex >> 3);
		UINT32 bitIndex = (~trackBitIndex) & 0x7;
		trackData[byteIndex] &= ~(1 << bitIndex);
		trackData[byteIndex] |= value << bitIndex;
		
		isDiskModified = true;
	}
	
	trackBitIndex++;
	if (trackBitIndex >= trackBitNum)
		trackBitIndex = 0;
}

void FloppyDiskDrive::updateReceiveBitNum(UINT32 * value) {
	if (*value >= trackBitNum)
		*value = (*value % trackBitNum);
}

bool FloppyDiskDrive::receiveBit() {
	UINT32 byteIndex = trackBitIndex >> 3;
	UINT32 bitIndex = (~trackBitIndex) & 0x7;
	bool bit = (trackData[byteIndex] >> bitIndex) & 0x1;
	getMC3470SpuriousBit(&bit);
	
	trackBitIndex++;
	if (trackBitIndex >= trackBitNum)
		trackBitIndex = 0;
	
	return bit;
}

void FloppyDiskDrive::discardBits(UINT32 bitNum) {
	trackBitIndex = (trackBitIndex + bitNum) % trackBitNum;
}

void FloppyDiskDrive::updateDiskTrack() {
	diskImage->getTrackData(&trackData, &trackBitNum, &trackBitIndex);
}

void FloppyDiskDrive::getMC3470SpuriousBit(bool * value) {
	if (*value)
		zeroBitCount = 0;
	else {
		zeroBitCount++;
		if (zeroBitCount > 3)
			*value = (random() & 0x1f) == 0;
	}
}
